﻿/*
 * TextServices / TSDotNet / TSNative
 * Marco Maria Bellocchi, Mauro Bampo
 * Copyright(c) 2013.
 * 
 * The MIT License
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 */

using System;
using TSDotNet.Common;

namespace TSDotNet.Encoders
{
    public class NumericStringZipper : IEncoder
    {
        private IWordEncoder<char, byte> _encoder;
        private ICharByteMapping _stringCode;

        public NumericStringZipper(IWordEncoder<char, byte> encoder, ICharByteMapping stringCode)
        {
            if (encoder == null)
                throw new ArgumentNullException("encoder == null");
            if (stringCode == null)
                throw new ArgumentNullException("stringCode == null");
            encoder.CheckIsValid();
            _encoder = encoder;
            _stringCode = stringCode;
        }

        public string Name
        {
            get { return "NumericStringZipper"; }
        }

        public string Encode(string data)
        {
            char[] chars = data.ToCharArray();
            int charlen = chars.Length;
            int bytesSize = charlen % 2 == 0 ? charlen : charlen + 1;
            int len = bytesSize - 2;
            char[] res = new char[bytesSize / 2];
            int k = 0;
            int i = 0;
            byte b1;
            byte b2;
            while (k < len)
            {
                b1 = _encoder.Encode(chars[k++]);
                b2 = _encoder.Encode(chars[k++]);
                res[i++] = _stringCode.GetChar((byte)(b1 | (b2 << 4)));
            }
            b1 = _encoder.Encode(chars[k++]);
            b2 = charlen % 2 == 1 ? _encoder.EncodeFiller() : _encoder.Encode(chars[k]);
            res[i] = _stringCode.GetChar((byte)(b1 | (b2 << 4)));
            return new string(res);
        }

        public string Decode(string data)
        {
            char[] chars = data.ToCharArray();
            int charlen = chars.Length;
            int charlenMinus1 = charlen - 1;
            int i = 0;
            byte last = _stringCode.GetByte(chars[charlenMinus1]);
            byte fillerByte = (byte)((last & 240) >> 4);
            char[] res = null;
            if (_encoder.IsFillerByte(fillerByte))
            {
                int l = charlen * 2 - 1;
                res = new char[l];
            }
            else
            {
                int l = charlen * 2;
                res = new char[l];
                res[l - 1] = _encoder.Decode(fillerByte);
            }
            int k = 0;
            for (; i < charlenMinus1; i++)
            {
                byte b = _stringCode.GetByte(chars[i]);
                res[k++] = _encoder.Decode((byte)(b & 15));
                res[k++] = _encoder.Decode((byte)((b & 240) >> 4));
            }
            res[k] = _encoder.Decode((byte)(last & 15));
            return new string(res);
        }
    }
}
/*
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TSDotNet.Encoders
{
    public class NumericStringZipper : IZipper
    {
        private IEncoder<char, byte> _encoder;
        private IStringCode _stringCode;
        private byte[] _buffer = new byte[2048];
        private byte[] _result = new byte[2048];

        public NumericStringZipper(IEncoder<char, byte> encoder, IStringCode stringCode)
        {
            if (encoder == null)
                throw new ArgumentNullException("encoder == null");
            if (stringCode == null)
                throw new ArgumentNullException("stringCode == null");
            encoder.CheckIsValid();
            _encoder = encoder;
            _stringCode = stringCode;
        }

        public string Name
        {
            get { return "NumericStringZipper"; }
        }

        public string Zip(string data)
        {
            char[] chars = data.ToCharArray();
            int charlen = chars.Length;
            int bytesSize = charlen % 2 == 0 ? charlen : charlen + 1;
            //byte[] bytes = new byte[bytesSize];
            int i = 0;
            for (; i < charlen; i++)
            {
                _buffer[i] = _encoder.Encode(chars[i]);
            }
            if (charlen % 2 == 1)
                _buffer[i++] = _encoder.EncodeFiller();
            //byte[] results = new byte[i / 2];
            int k = 0;
            i = 0;
            while (k < bytesSize)
            {
                _result[i++] = (byte)(_buffer[k++] | (_buffer[k++] << 4));
            }
            StringBuilder sb = new StringBuilder(i,i+1);
            for(int t = 0; t < i; t++)
            {
                sb.Append(_stringCode.GetChar(_result[t]));
            }
            return sb.ToString();
        }

        public string Unzip(string data)
        {
     
            char[] chars = data.ToCharArray();
            int charlen = chars.Length;
            //byte[] bytes = new byte[chars.Length];
            for (int i = 0; i < charlen; i++)
            {
                _buffer[i] = _stringCode.GetByte(chars[i]);
            }
            //byte[] results = new byte[chars.Length * 2];
            int k = 0;
            for (int i = 0; i < charlen; i++)
            {
                _result[k++] = (byte)(_buffer[i] & (byte)15);
                _result[k++] = (byte)((_buffer[i] & (byte)240) >> 4);
            }
            int len = charlen * 2;
            StringBuilder sb = new StringBuilder(len, len+1);
            int t = 0;
            for (; t < len-1; t++)
            {
                sb.Append(_encoder.Decode(_result[t]));
            }
            if(!_encoder.IsFillerByte(_result[t]))
                sb.Append(_encoder.Decode(_result[t]));
            return sb.ToString();
        }
    }
}

 */
/*
  public string Zip(string data)
        {
            char[] chars = data.ToCharArray();
            int charlen = chars.Length;
            int bytesSize = charlen % 2 == 0 ? charlen : charlen + 1;
            int i = 0;
            for (; i < charlen; i++)
            {
                _buffer[i] = _encoder.Encode(chars[i]);
            }
            if (charlen % 2 == 1)
                _buffer[i] = _encoder.EncodeFiller();
            int k = 0;
            StringBuilder sb = new StringBuilder(i / 2);
            while (k < bytesSize)
            {
                sb.Append(_stringCode.GetChar((byte)(_buffer[k++] | (_buffer[k++] << 4))));
            }
            return sb.ToString();
        }

        public string Unzip(string data)
        {
            char[] chars = data.ToCharArray();
            int charlen = chars.Length;
            int k = 0;
            for (int i = 0; i < charlen; i++)
            {
                byte b = _stringCode.GetByte(chars[i]);
                _result[k++] = (byte)(b & (byte)15);
                _result[k++] = (byte)((b & (byte)240) >> 4);
            }
            int len = charlen * 2;
            StringBuilder sb = new StringBuilder(len);
            int t = 0;
            for (; t < len-1; t++)
            {
                sb.Append(_encoder.Decode(_result[t]));
            }
            if(!_encoder.IsFillerByte(_result[t]))
                sb.Append(_encoder.Decode(_result[t]));
            return sb.ToString();
        }
 */